﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using GeoRef;
using Security;
using Storage;
using Stats;
using System.Transactions;

namespace Groupware
{
    /// <summary>
    /// This class represents a confirmed reservation on a trip
    /// </summary>
    public class Reservation
    {
        private int userId;
        private String username;
        private int tripId;
        private GeoLoc start;
        private GeoLoc end;
        private List<int> stages;
        private uint seatsNo;

        /// <summary>
        /// The passenger user id
        /// </summary>
        public int UserId
        {
            get { return userId; }
            internal set { userId = value; }
        }

        /// <summary>
        /// The username of the passenger
        /// </summary>
        public String Username
        {
            get { return username; }
            internal set { username = value; }
        }

        /// <summary>
        /// The Id of the trip which this reservation was made on
        /// </summary>
        public int TripId
        {
            get { return tripId; }
            internal set { tripId = value; }
        }

        /// <summary>
        /// The trip which this reservation was made on
        /// </summary>
        public Trip Trip
        {
            get
            {
                Storage.EntityModels.Trip dbtrip = null;
                try
                {
                    dbtrip = StorageManager.Instance.GetTripById(tripId);
                }
                catch (Storage.Exceptions.TripNotFoundException) { }
                return new Trip(dbtrip);
            }
        }

        /// <summary>
        /// The passenger getting in location
        /// </summary>
        public GeoLoc Start
        {
            get { return start; }
            internal set { start = value; }
        }

        /// <summary>
        /// The passenger getting off location
        /// </summary>
        public GeoLoc End {
            get { return end; }
            internal set { end = value; }
        }

        /// <summary>
        /// The number of seats reserved by the passenger
        /// </summary>
        public uint SeatsNo
        {
            get { return seatsNo; }
            internal set { seatsNo = value; }
        }

        /// <summary>
        /// The ids of the trip stages which the passenger will be in
        /// </summary>
        public List<int> StagesId
        {
            get { return stages; }
            internal set { stages = value; }
        }

        /// <summary>
        /// Revoke a reservation
        /// </summary>
        /// <param name="user">
        /// the user who made the operation
        /// (either the passenger or the owner of the trip related to this reservation)</param>
        /// <exception cref="OperationNotAllowedException">
        /// if the given user is neither the passenger, nor the trip owner for this reservation 
        /// </exception>
        /// <exception cref="Trip.StillAccomplishedException">
        /// if the trip related to this reservation is still accomplished
        /// </exception>
        public void Revoke(TripLoaUser user)
        {
            bool isOwner;
            if (user == null)
                throw new ArgumentNullException("user");
            Storage.EntityModels.Trip t = StorageManager.Instance.GetTripById(tripId);
            if (t == null) return;
            if (user.UserID == userId)
                isOwner = false;
            else if (t.Owner.Id == user.UserID)
                isOwner = true;
            else
                throw new OperationNotAllowedException("Only trip owner or reserved user can revoke a reservation");
            if (Trip.Accomplished)
                throw new Trip.StillAccomplishedException(/*"The trip related to this reservation is still accomplished"*/);
            
            using (TransactionScope transaction = new TransactionScope())
            {
                if (isOwner)
                {
                    OwnerRevoke();
                    StatsModule.deletePendingFeedbacksFromThisUserForTrip(t.OwnerId.Value, tripId);
                }
                else
                {
                    PassengerRevoke();
                    StatsModule.deletePendingFeedbacksFromThisUserForTrip(userId, tripId);
                }
                
                transaction.Complete();
            }
        }

        private void PassengerRevoke()
        {
            if (Destroy())
            {
                // Send notification to trip owner
                // set negative feedback to user???
                StorageManager.Instance.Entities.SubmitChanges();
            }
        }

        private void OwnerRevoke()
        {
            if (Destroy())
            {
                // Send notification to (not anymore) reserved user
                // set negative feedback to owner???
                StorageManager.Instance.Entities.SubmitChanges();
            }
        }

        /// <summary>
        /// Performs delete on db
        /// </summary>
        /// <returns>true if operation is done, false otherwise (the db entity doesn't exist)</returns>
        internal bool Destroy()
        {
            Storage.EntityModels.TripLoaDataContext db = StorageManager.Instance.Entities;
            int userId = StorageManager.Instance.GetUserIdByUserName(username);
            var q = from Storage.EntityModels.Users_Stages us in db.Users_Stages
                    where us.Stage.TripId == tripId && us.UserId == userId
                    select us;
            if (q.Count() == 0)
                return false;
            db.Users_Stages.DeleteAllOnSubmit(q);
            return true;
        }
    }
}
